Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 | 2x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | import { NextResponse } from 'next/server'
import { Prisma } from '@prisma/client'
import { prisma } from '@/lib/prisma'
import { checkAdminAuth } from '@/lib/auth-check'
import { validateOrderStatus, ValidationError } from '@/lib/validation'
import type { ColorVariant, SizeVariant } from '@/types'
type Params = {
params: Promise<{
id: string
}>
}
type UpdateOrderItemInput = {
productId: string
quantity: number
price: number | string
selectedColor?: string | null
selectedSize?: string | null
}
type UpdateOrderBody = {
status?: string
customerName?: string
customerEmail?: string
customerPhone?: string
customerAddress?: string
customerCity?: string
customerZip?: string
shippingMethod?: string
notes?: string | null
total?: number | string
items?: UpdateOrderItemInput[]
}
const toNumber = (value: number | string): number =>
typeof value === 'string' ? parseFloat(value) : value
const toSizeVariants = (value: unknown): SizeVariant[] =>
Array.isArray(value) ? (value as SizeVariant[]) : []
const toColorVariants = (value: unknown): ColorVariant[] =>
Array.isArray(value) ? (value as ColorVariant[]) : []
// GET /api/admin/orders/[id] - Get single order (Admin only)
export async function GET(_request: Request, { params }: Params) {
const authError = await checkAdminAuth()
Iif (authError) return authError
try {
const { id } = await params
const order = await prisma.order.findUnique({
where: { id },
include: {
items: {
include: {
product: true,
},
},
},
})
Eif (!order) {
return NextResponse.json({ error: 'Order not found' }, { status: 404 })
}
return NextResponse.json(order)
} catch (error) {
console.error('Error fetching order:', error)
return NextResponse.json({ error: 'Failed to fetch order' }, { status: 500 })
}
}
// PUT /api/admin/orders/[id] - Update order (all fields) (Admin only)
export async function PUT(request: Request, { params }: Params) {
const authError = await checkAdminAuth()
Iif (authError) return authError
try {
const { id } = await params
const body = (await request.json()) as UpdateOrderBody
// Validate order status
const status = validateOrderStatus(body.status)
// Get current order to check previous status
const currentOrder = await prisma.order.findUnique({
where: { id },
include: {
items: {
include: {
product: true,
},
},
},
})
Iif (!currentOrder) {
return NextResponse.json({ error: 'Order not found' }, { status: 404 })
}
// Check if status is changing from pre-shipped to shipped/delivered
const preShippedStatuses = ['pending', 'processing']
const shippedStatuses = ['shipped', 'delivered']
const shouldReduceStock =
preShippedStatuses.includes(currentOrder.status) &&
shippedStatuses.includes(status)
// Build update data for all editable fields
const updateData: Prisma.OrderUpdateInput = { status }
Iif (body.customerName !== undefined) updateData.customerName = body.customerName
Iif (body.customerEmail !== undefined) updateData.customerEmail = body.customerEmail
Iif (body.customerPhone !== undefined) updateData.customerPhone = body.customerPhone
Iif (body.customerAddress !== undefined) updateData.customerAddress = body.customerAddress
Iif (body.customerCity !== undefined) updateData.customerCity = body.customerCity
Iif (body.customerZip !== undefined) updateData.customerZip = body.customerZip
Iif (body.shippingMethod !== undefined) updateData.shippingMethod = body.shippingMethod
Iif (body.notes !== undefined) updateData.notes = body.notes || null
Iif (body.total !== undefined) updateData.total = toNumber(body.total)
// Handle items update: delete old items and create new ones
Iif (Array.isArray(body.items)) {
const itemsToCreate: Prisma.OrderItemUncheckedCreateWithoutOrderInput[] = body.items.map((item) => ({
productId: item.productId,
quantity: item.quantity,
price: toNumber(item.price),
selectedColor: item.selectedColor || null,
selectedSize: item.selectedSize || null,
}))
updateData.items = {
deleteMany: {},
create: itemsToCreate,
}
}
// Update order
const order = await prisma.order.update({
where: { id },
data: updateData,
include: {
items: {
include: {
product: true,
},
},
},
})
// Reduce stock if status changed to shipped/delivered
Iif (shouldReduceStock) {
for (const orderItem of currentOrder.items) {
const item = orderItem
const product = item.product
let handledByVariant = false
// Both size + color: use nested colorStocks inside the size variant
if (item.selectedSize && item.selectedColor && product.sizes) {
const sizes = toSizeVariants(product.sizes)
const sizeVariant = sizes.find((s) => s.name === item.selectedSize)
if (sizeVariant?.colorStocks) {
// Decrement the specific colorStocks entry
const updatedSizes: SizeVariant[] = sizes.map((size) => {
if (size.name === item.selectedSize) {
const colorStocks = size.colorStocks ?? []
return {
...size,
colorStocks: colorStocks.map((cs) =>
cs.colorName === item.selectedColor
? { ...cs, stock: Math.max(0, cs.stock - item.quantity) }
: cs
),
}
}
return size
})
await prisma.product.update({
where: { id: item.productId },
data: { sizes: updatedSizes as unknown as Prisma.InputJsonValue },
})
handledByVariant = true
}
}
// Size only (no colorStocks)
if (!handledByVariant && item.selectedSize && product.sizes) {
const sizes = toSizeVariants(product.sizes)
const updatedSizes: SizeVariant[] = sizes.map((size) => {
if (size.name === item.selectedSize) {
return { ...size, stock: Math.max(0, size.stock - item.quantity) }
}
return size
})
await prisma.product.update({
where: { id: item.productId },
data: { sizes: updatedSizes as unknown as Prisma.InputJsonValue },
})
handledByVariant = true
}
// Color only (no sizes)
if (!handledByVariant && item.selectedColor && product.colors) {
const colors = toColorVariants(product.colors)
const updatedColors: ColorVariant[] = colors.map((color) => {
if (color.name === item.selectedColor) {
return { ...color, stock: Math.max(0, color.stock - item.quantity) }
}
return color
})
await prisma.product.update({
where: { id: item.productId },
data: { colors: updatedColors as unknown as Prisma.InputJsonValue },
})
handledByVariant = true
}
// Reduce flat stock only if no variant was used
if (!handledByVariant) {
await prisma.product.update({
where: { id: item.productId },
data: { stock: { decrement: item.quantity } },
})
}
}
}
return NextResponse.json(order)
} catch (error) {
console.error('Error updating order:', error)
if (error instanceof ValidationError) {
return NextResponse.json({ error: error.message }, { status: 400 })
}
return NextResponse.json({ error: 'Failed to update order' }, { status: 500 })
}
}
// DELETE /api/admin/orders/[id] - Delete order (Admin only)
export async function DELETE(_request: Request, { params }: Params) {
const authError = await checkAdminAuth()
if (authError) return authError
try {
const { id } = await params
const order = await prisma.order.findUnique({ where: { id } })
if (!order) {
return NextResponse.json({ error: 'Order not found' }, { status: 404 })
}
await prisma.order.delete({ where: { id } })
return NextResponse.json({ success: true })
} catch (error) {
console.error('Error deleting order:', error)
return NextResponse.json({ error: 'Failed to delete order' }, { status: 500 })
}
}
|